home *** CD-ROM | disk | FTP | other *** search
/ PC User 2003 January / Disc 1 / PCU0103CD1.iso / entertn / demos / files / aomtrial.exe / AOM / AI / SCN22P4.XS < prev    next >
Encoding:
Text File  |  2002-06-03  |  21.0 KB  |  570 lines

  1. //==============================================================================
  2. // Scn22p4: AI Scenario Script for scenario 22 player 4
  3. //==============================================================================
  4.  
  5. //==============================================================================
  6. // Set Town Location
  7. //==============================================================================
  8. void setTownLocation(void)
  9. {
  10.    //Look for the "Town Location" marker.
  11.    kbSetTownLocation(kbGetBlockPosition("1840"));
  12. }
  13.  
  14. //==============================================================================
  15. // miscStartup
  16. //==============================================================================
  17. void miscStartup(void)
  18. {
  19.     // Difficulty Level check.
  20.     int difflevel=-1;        
  21.     difflevel=aiGetWorldDifficulty();
  22.  
  23.    //Startup message(s).
  24.    aiEcho("");
  25.    aiEcho("");
  26.    aiEcho("Scn22P4 AI Start, filename='"+cFilename+"'.");
  27.     aiEcho("Difficulty Level="+difflevel+".");
  28.    //Spit out the map size.
  29.    aiEcho("  Map size is ("+kbGetMapXSize()+", "+kbGetMapZSize()+").");
  30.    //Cheat like a bastard.  Once only, though.
  31.    kbLookAtAllUnitsOnMap();
  32.    //Calculate some areas.
  33.    kbAreaCalculate(1200.0);
  34.    //Set our town location.
  35.    setTownLocation();
  36.     //Reset random seed
  37.     aiRandSetSeed();
  38.    //Allocate all resources to the root escrow.
  39.    kbEscrowAllocateCurrentResources();
  40. }
  41.  
  42.  
  43. //==============================================================================
  44. //==============================================================================
  45. // Attack stuff.
  46. //==============================================================================
  47. //==============================================================================
  48. //Shared variables.
  49. int numberAttacks=0;
  50. int attackPlayerID=-1;
  51.  
  52. //TODO: Decide how to rep attack group size.
  53. int attackMinimumGroupSize=3;
  54. int attackMaximumGroupSize=5;
  55.  
  56. //Attack 1 vars.
  57. int attackPlan1ID=-1;
  58. int maintainPlan1ID=-1;
  59.  
  60. //Attack 2 vars.
  61. int attackPlan2ID=-1;
  62. int maintainPlan2ID=-1;
  63.  
  64. // Route and path vars
  65. int attackRoute1ID=-1;
  66. int attackPath1ID=-1;
  67. int attackRoute2ID=-1;
  68. int attackPath2ID=-1;
  69.  
  70. // Unit types
  71. int attackerUnitTypeID1=cUnitTypeSpearman;
  72. int attackerUnitTypeID2=cUnitTypeSlinger;
  73. int attackerUnitTypeID3=cUnitTypeCamelry;
  74. int attackerUnitTypeID4=cUnitTypeChariotArcher;
  75. int attackerUnitTypeID5=cUnitTypeWarElephant;
  76.  
  77. //=========================================================================================
  78. // Kidd's cool configQuery function: used to create attack routes, etc.  Oooh, lovin' that!
  79. //=========================================================================================
  80. bool configQuery( int queryID = -1, int unitType = -1, int action = -1, int state = -1, int player = -1, vector center = vector(-1,-1,-1), bool sort = false, float radius = -1 )
  81. {
  82.    if ( queryID == -1)
  83.    {
  84.       return(false);
  85.    }
  86.  
  87.    if (player != -1)
  88.       kbUnitQuerySetPlayerID(queryID, player);
  89.    
  90.    if (unitType != -1)
  91.       kbUnitQuerySetUnitType(queryID, unitType);
  92.  
  93.    if (action != -1)
  94.       kbUnitQuerySetActionType(queryID, action);
  95.  
  96.    if (state != -1)
  97.       kbUnitQuerySetState(queryID, state);
  98.  
  99.    if (center != vector(-1,-1,-1))
  100.    {
  101.       kbUnitQuerySetPosition(queryID, center);
  102.       if (sort == true)
  103.          kbUnitQuerySetAscendingSort(queryID, true);
  104.       if (radius != -1)
  105.          kbUnitQuerySetMaximumDistance(queryID, radius);
  106.    }
  107.    return(true);
  108. }
  109.  
  110. //==============================================================================
  111. // initAttack: Creates attack routes, etc.
  112. //==============================================================================
  113. void initAttack(int playerID=-1)
  114. {
  115.    //Destroy all previous attacks (if this isn't the player we're already attacking.
  116.    if (playerID != attackPlayerID)
  117.    {
  118.       //Reset the attack player ID.
  119.       attackPlayerID=-1;
  120.       //Destroy any previous attack plan.
  121.       aiPlanDestroy(attackPlan1ID);
  122.       attackPlan1ID=-1;
  123.       aiPlanDestroy(attackPlan2ID);
  124.       attackPlan2ID=-1;
  125.   
  126.       //Destroy our previous attack paths.
  127.       kbPathDestroy(attackPath1ID);
  128.       attackPath1ID=-1;
  129.       kbPathDestroy(attackPath2ID);
  130.       attackPath2ID=-1;
  131.  
  132.       //Destroy our previous attack routes.
  133.       attackRoute1ID=-1;
  134.       attackRoute2ID=-1;
  135.  
  136.       //Reset the number of attacks.
  137.       numberAttacks=0;
  138.    }
  139.  
  140.    //Save the player to attack.
  141.    attackPlayerID=playerID;
  142.  
  143.    vector gatherPointForward=kbGetBlockPosition("1844");
  144.     vector gatherPointRear=kbGetBlockPosition("1840");
  145.        
  146.     //Setup attack path 1 - N/A; forward attack (no waypoints)
  147.    attackPath1ID=kbPathCreate("Attack Path 1");
  148.    //Create attack route 1 (from gather point)
  149.    attackRoute1ID=kbCreateAttackRouteWithPath("Attack Route 1", gatherPointForward, kbGetBlockPosition("1845"));
  150.    
  151.     if (attackRoute1ID >= 0)
  152.       kbAttackRouteAddPath(attackRoute1ID, attackPath1ID);
  153.  
  154.    //Setup attack path 2 - from the rear
  155.    attackPath2ID=kbPathCreate("Attack Path 2");
  156.    kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("1841"));
  157.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("1842"));
  158.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("1843"));
  159.     kbPathAddWaypoint(attackPath2ID, kbGetBlockPosition("1844"));
  160.     
  161.    //Create attack route 2.
  162.    attackRoute2ID=kbCreateAttackRouteWithPath("Attack Route 2", gatherPointRear, kbGetBlockPosition("1845"));
  163.    
  164.     if (attackRoute2ID >= 0)
  165.       kbAttackRouteAddPath(attackRoute2ID, attackPath2ID);
  166. }
  167.  
  168. //==============================================================================
  169. // setupForwardAttack
  170. //==============================================================================
  171. bool setupForwardAttack(int playerID=-1)
  172. {
  173.     //Info.
  174.     aiEcho("Attacking Player "+playerID+".");
  175.  
  176.    //If the player to attack doesn't match, init the attack.
  177.    if (attackPlayerID != playerID)
  178.    {
  179.       initAttack(playerID);
  180.       if (attackPlayerID < 0)
  181.          return(false);
  182.    }
  183.  
  184.    //Create an attack plan.
  185.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  186.    if (newAttackPlanID < 0)
  187.       return(false);
  188.  
  189.    //Target player (required).  This must work.
  190.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  191.       return(false);
  192.  
  193.    //Gather point.
  194.     vector gatherPoint=kbGetBlockPosition("1844");
  195.  
  196.     //Set the target type.  This must work.
  197.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  198.       return(false);
  199.  
  200.    //Unit types to attack.
  201.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  202.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  203.  
  204.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute1ID);
  205.    
  206.    //Set the gather point and gather point distance.
  207.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  208.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 15.0);
  209.  
  210.    //Set up the attack route usage pattern.
  211.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  212.    
  213.     //Add the unit types to the plan - always spearmen, whatever slingers are available.
  214.    aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID1, attackMinimumGroupSize, attackMaximumGroupSize, attackMaximumGroupSize);
  215.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID2, 0, attackMaximumGroupSize, attackMaximumGroupSize);
  216.     
  217.    //Set the initial position.
  218.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  219.    //Plan requires all need units to work (can be false).
  220.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  221.    //Activate the plan.
  222.    aiPlanSetActive(newAttackPlanID);
  223.  
  224.    //Now, save the attack plan ID appropriately.
  225.    aiPlanSetOrphan(attackPlan1ID, true);
  226.    attackPlan1ID=newAttackPlanID;
  227.  
  228.    //Increment our overall number of attacks.
  229.    numberAttacks++;
  230. }
  231.  
  232.  
  233. //==============================================================================
  234. // setupRearAttack
  235. //==============================================================================
  236. bool setupRearAttack(int playerID=-1)
  237. {
  238.     //Info.
  239.     aiEcho("Attacking Player "+playerID+".");
  240.  
  241.    //If the player to attack doesn't match, init the attack.
  242.    if (attackPlayerID != playerID)
  243.    {
  244.       initAttack(playerID);
  245.       if (attackPlayerID < 0)
  246.          return(false);
  247.    }
  248.  
  249.    //Create an attack plan.
  250.    int newAttackPlanID=aiPlanCreate("Attack Player"+attackPlayerID+" Attempt"+numberAttacks, cPlanAttack);
  251.    if (newAttackPlanID < 0)
  252.       return(false);
  253.  
  254.    //Target player (required).  This must work.
  255.    if (aiPlanSetVariableInt(newAttackPlanID, cAttackPlanPlayerID, 0, attackPlayerID) == false)
  256.       return(false);
  257.  
  258.    //Gather point.
  259.     vector gatherPoint=kbGetBlockPosition("1840");
  260.  
  261.     //Set the target type.  This must work.
  262.    if (aiPlanSetNumberVariableValues(newAttackPlanID, cAttackPlanTargetTypeID, 2, true) == false)
  263.       return(false);
  264.  
  265.    //Unit types to attack.
  266.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 0, cUnitTypeUnit);
  267.     aiPlanSetVariableInt(newAttackPlanID, cAttackPlanTargetTypeID, 1, cUnitTypeBuilding);
  268.  
  269.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRouteID, 0, attackRoute2ID);
  270.    
  271.    //Set the gather point and gather point distance.
  272.    aiPlanSetVariableVector(newAttackPlanID, cAttackPlanGatherPoint, 0, gatherPoint);
  273.    aiPlanSetVariableFloat(newAttackPlanID, cAttackPlanGatherDistance, 0, 15.0);
  274.  
  275.    //Set up the attack route usage pattern
  276.    aiPlanSetVariableInt(newAttackPlanID, cAttackPlanAttackRoutePattern, 0, cAttackPlanAttackRoutePatternRandom);
  277.    
  278.     //Add the unit types to the plan.  Camels and chariots and elephants, oh my.
  279.    aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID3, 1, 2, 4);
  280.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID4, 0, 2, 3);
  281.     aiPlanAddUnitType(newAttackPlanID, attackerUnitTypeID5, 0, 1, 2);
  282.     
  283.    //Set the initial position.
  284.    aiPlanSetInitialPosition(newAttackPlanID, gatherPoint);
  285.    //Plan requires all need units to work (can be false).
  286.    aiPlanSetRequiresAllNeedUnits(newAttackPlanID, true);
  287.    //Activate the plan.
  288.    aiPlanSetActive(newAttackPlanID);
  289.  
  290.    //Now, save the attack plan ID appropriately.
  291.    aiPlanSetOrphan(attackPlan2ID, true);
  292.    attackPlan2ID=newAttackPlanID;
  293.  
  294.    //Increment our overall number of attacks.
  295.    numberAttacks++;
  296. }
  297.  
  298. //==============================================================================
  299. // Queryin' for enemies near the first TC (for Ancestors)
  300. //==============================================================================
  301. int checkForEnemies(void)
  302. {
  303.     static int enemyQueryID=-1;
  304.     vector firstTC=kbGetBlockPosition("1844");
  305.     int enemyCount=-1;
  306.  
  307.    if (enemyQueryID < 0)
  308.    {  
  309.         // Doesn't exist, set it up
  310.       enemyQueryID = kbUnitQueryCreate("Enemies at TC Query");
  311.         
  312.       // Get the number
  313.       if ( configQuery( enemyQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 1, firstTC, false, 15 ) == false )
  314.          return(-1);
  315.    }
  316.  
  317.    kbUnitQueryResetResults(enemyQueryID);
  318.    enemyCount = kbUnitQueryExecute(enemyQueryID);
  319.     return(enemyCount);
  320. }
  321.  
  322. //==============================================================================
  323. // Queryin' for enemies at the gate (to trigger attacks)
  324. //==============================================================================
  325. int checkForEnemiesAtGate(void)
  326. {
  327.     static int enemyQueryID=-1;
  328.     vector mainGate=kbGetBlockPosition("1845");
  329.     int enemyCount=-1;
  330.  
  331.    if (enemyQueryID < 0)
  332.    {  
  333.         // Doesn't exist, set it up
  334.       enemyQueryID = kbUnitQueryCreate("Enemies at Gate Query");
  335.         
  336.       // Get the number
  337.       if ( configQuery( enemyQueryID, cUnitTypeUnit, -1, cUnitStateAlive, 1, mainGate, false, 15 ) == false )
  338.          return(-1);
  339.    }
  340.  
  341.    kbUnitQueryResetResults(enemyQueryID);
  342.    enemyCount = kbUnitQueryExecute(enemyQueryID);
  343.     return(enemyCount);
  344. }
  345.  
  346. //==============================================================================
  347. // Rule to check for enemies close to the TC - if so, use Ancestors at the TC.
  348. //==============================================================================
  349. rule castAncestors
  350.     minInterval 10
  351.     inactive
  352.     group GodPowerRules
  353. {
  354.     int numEnemies=-1;
  355.     vector ancestorsPoint=kbGetBlockPosition("1844");
  356.     numEnemies=checkForEnemies();
  357.  
  358.     if (numEnemies > 4)
  359.     {
  360.         aiEcho("Enemies close to TC, cast Ancestors.");
  361.         if ( aiCastGodPowerAtPosition(cTechSkeletonPower, ancestorsPoint) == true )
  362.             xsDisableSelf();
  363.         else
  364.             aiEcho("Ancestors failed - try again later.");
  365.     }
  366. }
  367.  
  368. //==============================================================================
  369. // Fire Vision, two minutes in.
  370. //==============================================================================
  371. rule castVision
  372.     minInterval 120
  373.     active
  374.     group GodPowerRules
  375. {
  376.     vector visionPoint=kbGetBlockPosition("1847");
  377.     aiEcho("Cast Vision near enemy TC");
  378.     if ( aiCastGodPowerAtPosition(cTechVision, visionPoint) == true )
  379.         xsDisableSelf();
  380.     else
  381.         aiEcho("Vision failed - try again later.");
  382. }
  383.  
  384. //==============================================================================
  385. // Attack Generator 1 - Forward Infantry Group
  386. //==============================================================================
  387. rule attackGenerator1
  388.    minInterval 75
  389.    inactive
  390.    group AttackRules
  391.    runImmediately
  392. {
  393.    //See how many "idle" attack plans we have.  Don't create any more if we have
  394.    //idle plans.
  395.    // int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  396.  
  397.    // DP's check taken out.  Let them be idle!!!
  398.     // if (numberIdleAttackPlans > 0)
  399.     //   return;
  400.  
  401.    //If we have enough unassigned military units, create a new attack plan.
  402.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID1);
  403.    aiEcho("There are "+numberAvailableUnits+" spearmen available for a new attack.");
  404.    
  405.     if (numberAvailableUnits >= attackMinimumGroupSize)
  406.         setupForwardAttack(1);
  407. }
  408.  
  409. //==============================================================================
  410. // Attack Generator 2 - Cavalry Group
  411. //==============================================================================
  412. rule attackGenerator2
  413.    minInterval 90
  414.    inactive
  415.    group AttackRules
  416.     runImmediately
  417. {
  418.    //See how many "idle" attack plans we have.  Don't create any more if we have
  419.    //idle plans.
  420.    // int numberIdleAttackPlans=aiGetNumberIdlePlans(cPlanAttack);
  421.  
  422.     // DP's check taken out.  Let them be idle!!!
  423.    // if (numberIdleAttackPlans > 0)
  424.    // return;
  425.  
  426.    //If we have enough unassigned military units, create a new attack plan.
  427.    int numberAvailableUnits=aiNumberUnassignedUnits(attackerUnitTypeID3);
  428.    aiEcho("There are "+numberAvailableUnits+" camelry available for a new attack.");
  429.    
  430.     if (numberAvailableUnits >= 2)
  431.         setupRearAttack(1);
  432. }
  433.  
  434. //==============================================================================
  435. // Rule to check for three enemies close to the gate - activates initial attacks
  436. //==============================================================================
  437. rule attack1Enabler
  438.     minInterval 15
  439.     active
  440.     group AttackRules
  441. {
  442.     int numEnemies=-1;
  443.     numEnemies=checkForEnemiesAtGate();
  444.  
  445.     if (numEnemies > 2)
  446.     {
  447.         aiEcho("*** QUERY: Enemy at the gates, activating attacks.");
  448.         xsEnableRule("attackGenerator1");
  449.         xsEnableRule("castAncestors");
  450.         xsDisableSelf();
  451.     }
  452. }
  453.  
  454. //=====================================================================================
  455. // Attack Launcher.  This is called from the scenario with an AI Func effect
  456. // when the player severely damages the forward settlement.
  457. //=====================================================================================
  458. void attackLauncher(int whichAttack = -1)
  459. {
  460.     aiEcho("*** FORWARD SETTLEMENT DAMAGED - ATTACK LAUNCHER CALLED ***");
  461.     xsEnableRule("attackGenerator2");
  462. }
  463.  
  464. //==============================================================================
  465. // MAIN.
  466. //==============================================================================
  467. void main(void)
  468. {
  469.    //Startup.
  470.    miscStartup();
  471.  
  472.    //Share the number to maintain.
  473.    int numberToMaintain=attackMinimumGroupSize*2;
  474.  
  475.    //Two gather points possible.
  476.    vector gatherPointForward=kbGetBlockPosition("1844");
  477.     vector gatherPointRear=kbGetBlockPosition("1840");
  478.  
  479.    //Maintain X spearmen.
  480.    int maintainPlan1ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID1), cPlanTrain);
  481.    if (maintainPlan1ID >= 0)
  482.    {
  483.         //Must set the type of unit to train.
  484.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanUnitType, 0, attackerUnitTypeID1);
  485.       //You can limit the number of units that are ever trained by this plan with this call.
  486.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  487.       //Set the number of units to maintain in the world at one time.
  488.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  489.       //Don't train units too fast
  490.       aiPlanSetVariableInt(maintainPlan1ID, cTrainPlanFrequency, 0, 20);
  491.       //Set a gather point.
  492.       aiPlanSetVariableVector(maintainPlan1ID, cTrainPlanGatherPoint, 0, gatherPointForward);
  493.       //Activate the plan.
  494.       aiPlanSetActive(maintainPlan1ID);
  495.    }
  496.  
  497.     //Maintain X slingers.
  498.    int maintainPlan2ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID2), cPlanTrain);
  499.    if (maintainPlan2ID >= 0)
  500.    {
  501.         //Must set the type of unit to train.
  502.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanUnitType, 0, attackerUnitTypeID2);
  503.       //You can limit the number of units that are ever trained by this plan with this call.
  504.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  505.       //Set the number of units to maintain in the world at one time.
  506.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanNumberToMaintain, 0, numberToMaintain);
  507.       //Don't train units too fast
  508.       aiPlanSetVariableInt(maintainPlan2ID, cTrainPlanFrequency, 0, 30);
  509.       //Set a gather point.
  510.       aiPlanSetVariableVector(maintainPlan2ID, cTrainPlanGatherPoint, 0, gatherPointForward);
  511.       //Activate the plan.
  512.       aiPlanSetActive(maintainPlan2ID);
  513.    }
  514.  
  515.     //Maintain 4 camelry.
  516.    int maintainPlan3ID=aiPlanCreate("Maintain "+numberToMaintain+" "+kbGetProtoUnitName(attackerUnitTypeID3), cPlanTrain);
  517.    if (maintainPlan3ID >= 0)
  518.    {
  519.         //Must set the type of unit to train.
  520.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanUnitType, 0, attackerUnitTypeID3);
  521.       //You can limit the number of units that are ever trained by this plan with this call.
  522.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  523.       //Set the number of units to maintain in the world at one time.
  524.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanNumberToMaintain, 0, 4);
  525.       //Don't train units too fast
  526.       aiPlanSetVariableInt(maintainPlan3ID, cTrainPlanFrequency, 0, 30);
  527.       //Set a gather point.
  528.       aiPlanSetVariableVector(maintainPlan3ID, cTrainPlanGatherPoint, 0, gatherPointRear);
  529.       //Activate the plan.
  530.       aiPlanSetActive(maintainPlan3ID);
  531.    }
  532.  
  533.     //Maintain 3 chariot archers.
  534.    int maintainPlan4ID=aiPlanCreate("Maintain 3 "+kbGetProtoUnitName(attackerUnitTypeID4), cPlanTrain);
  535.    if (maintainPlan4ID >= 0)
  536.    {
  537.         //Must set the type of unit to train.
  538.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanUnitType, 0, attackerUnitTypeID4);
  539.       //You can limit the number of units that are ever trained by this plan with this call.
  540.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  541.       //Set the number of units to maintain in the world at one time.
  542.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanNumberToMaintain, 0, 3);
  543.       //Don't train units too fast
  544.       aiPlanSetVariableInt(maintainPlan4ID, cTrainPlanFrequency, 0, 45);
  545.       //Set a gather point.
  546.       aiPlanSetVariableVector(maintainPlan4ID, cTrainPlanGatherPoint, 0, gatherPointRear);
  547.       //Activate the plan.
  548.       aiPlanSetActive(maintainPlan4ID);
  549.    }
  550.  
  551.     //Maintain 2 elephants
  552.    int maintainPlan5ID=aiPlanCreate("Maintain 2 "+kbGetProtoUnitName(attackerUnitTypeID5), cPlanTrain);
  553.    if (maintainPlan5ID >= 0)
  554.    {
  555.         //Must set the type of unit to train.
  556.       aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanUnitType, 0, attackerUnitTypeID5);
  557.       //You can limit the number of units that are ever trained by this plan with this call.
  558.       //aiPlanSetVariableInt(maintainPlanID, cTrainPlanNumberToTrain, 0, 25);
  559.       //Set the number of units to maintain in the world at one time.
  560.       aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanNumberToMaintain, 0, 2);
  561.       //Don't train units too fast
  562.       aiPlanSetVariableInt(maintainPlan5ID, cTrainPlanFrequency, 0, 60);
  563.       //Set a gather point.
  564.       aiPlanSetVariableVector(maintainPlan5ID, cTrainPlanGatherPoint, 0, gatherPointRear);
  565.       //Activate the plan.
  566.       aiPlanSetActive(maintainPlan5ID);
  567.    }
  568.  
  569. }
  570.